#!/usr/bin/env python
"""
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

"""

from resource_management.core.resources.system import Directory, Execute, File
from resource_management.libraries.resources.xml_config import XmlConfig
from resource_management.libraries.resources.template_config import TemplateConfig
from resource_management.core.source import InlineTemplate, Template
from resource_management.libraries.functions.format import format
from resource_management.libraries.functions.default import default
from resource_management.libraries.functions.generate_logfeeder_input_config import generate_logfeeder_input_config

import glob
import os


def ams(name=None, action=None):
    import params

    if name == 'collector':
        Directory(
            params.ams_collector_conf_dir,
            owner=params.ams_user,
            group=params.user_group,
            create_parents=True,
            recursive_ownership=True,
        )

        Directory(
            params.ams_checkpoint_dir,
            owner=params.ams_user,
            group=params.user_group,
            cd_access="a",
            create_parents=True,
            recursive_ownership=True)

        new_ams_site = {}
        new_ams_site.update(params.config['configurations']['ams-site'])
        if params.clusterHostInfoDict:
            master_components = []
            slave_components = []
            components = dict(params.clusterHostInfoDict).keys()
            known_slave_components = [
                "nodemanager", "metrics_monitor", "datanode",
                "hbase_regionserver"
            ]
            for component in components:
                if component and component.endswith("_hosts"):
                    component_name = component[:-6]
                elif component and component.endswith("_host"):
                    component_name = component[:-5]
                else:
                    continue
                if component_name in known_slave_components:
                    slave_components.append(component_name)
                else:
                    master_components.append(component_name)

            if slave_components:
                new_ams_site[
                    'timeline.metrics.initial.configured.slave.components'] = ",".join(
                        slave_components)
            if master_components:
                if 'ambari_server' not in master_components:
                    master_components.append('ambari_server')
                new_ams_site[
                    'timeline.metrics.initial.configured.master.components'] = ",".join(
                        master_components)

        hbase_total_heapsize_with_trailing_m = params.hbase_heapsize
        hbase_total_heapsize = int(
            hbase_total_heapsize_with_trailing_m[:-1]) * 1024 * 1024
        new_ams_site['hbase_total_heapsize'] = hbase_total_heapsize

        XmlConfig(
            "ams-site.xml",
            conf_dir=params.ams_collector_conf_dir,
            configurations=new_ams_site,
            configuration_attributes=params.config['configurationAttributes']
            ['ams-site'],
            owner=params.ams_user,
            group=params.user_group)

        XmlConfig(
            "ssl-server.xml",
            conf_dir=params.ams_collector_conf_dir,
            configurations=params.config['configurations']['ams-ssl-server'],
            configuration_attributes=params.config['configurationAttributes']
            ['ams-ssl-server'],
            owner=params.ams_user,
            group=params.user_group)

        merged_ams_hbase_site = {}
        merged_ams_hbase_site.update(
            params.config['configurations']['ams-hbase-site'])
        if params.security_enabled:
            merged_ams_hbase_site.update(
                params.config['configurations']['ams-hbase-security-site'])

        # Add phoenix client side overrides
        merged_ams_hbase_site['phoenix.query.maxGlobalMemoryPercentage'] = str(
            params.phoenix_max_global_mem_percent)
        merged_ams_hbase_site[
            'phoenix.spool.directory'] = params.phoenix_client_spool_dir

        XmlConfig(
            "hbase-site.xml",
            conf_dir=params.ams_collector_conf_dir,
            configurations=merged_ams_hbase_site,
            configuration_attributes=params.config['configurationAttributes']
            ['ams-hbase-site'],
            owner=params.ams_user,
            group=params.user_group)

        if params.security_enabled:
            TemplateConfig(
                os.path.join(params.hbase_conf_dir, "ams_collector_jaas.conf"),
                owner=params.ams_user,
                template_tag=None)

        if (params.log4j_props != None):
            File(
                format("{params.ams_collector_conf_dir}/log4j.properties"),
                mode=0644,
                group=params.user_group,
                owner=params.ams_user,
                content=InlineTemplate(params.log4j_props))

        File(
            format("{ams_collector_conf_dir}/ams-env.sh"),
            owner=params.ams_user,
            content=InlineTemplate(params.ams_env_sh_template))

        Directory(
            params.ams_collector_log_dir,
            owner=params.ams_user,
            group=params.user_group,
            cd_access="a",
            create_parents=True,
            mode=0755,
        )

        Directory(
            params.ams_collector_pid_dir,
            owner=params.ams_user,
            group=params.user_group,
            cd_access="a",
            create_parents=True,
            mode=0755,
        )

        # Hack to allow native HBase libs to be included for embedded hbase
        File(
            os.path.join(params.ams_hbase_home_dir, "bin", "hadoop"),
            owner=params.ams_user,
            mode=0755)

        # On some OS this folder could be not exists, so we will create it before pushing there files
        Directory(
            params.limits_conf_dir,
            create_parents=True,
            owner='root',
            group='root')

        # Setting up security limits
        File(
            os.path.join(params.limits_conf_dir, 'ams.conf'),
            owner='root',
            group='root',
            mode=0644,
            content=Template("ams.conf.j2"))

        # Phoenix spool file dir if not /tmp
        if not os.path.exists(params.phoenix_client_spool_dir):
            Directory(
                params.phoenix_client_spool_dir,
                owner=params.ams_user,
                mode=0755,
                group=params.user_group,
                cd_access="a",
                create_parents=True)
        pass

        if not params.is_local_fs_rootdir and params.is_ams_distributed:
            # Configuration needed to support NN HA
            XmlConfig(
                "hdfs-site.xml",
                conf_dir=params.ams_collector_conf_dir,
                configurations=params.config['configurations']['hdfs-site'],
                configuration_attributes=params.
                config['configurationAttributes']['hdfs-site'],
                owner=params.ams_user,
                group=params.user_group,
                mode=0644)

            XmlConfig(
                "hdfs-site.xml",
                conf_dir=params.hbase_conf_dir,
                configurations=params.config['configurations']['hdfs-site'],
                configuration_attributes=params.
                config['configurationAttributes']['hdfs-site'],
                owner=params.ams_user,
                group=params.user_group,
                mode=0644)

            # Remove spnego configs from core-site if platform does not have python-kerberos library
            truncated_core_site = {}
            truncated_core_site.update(
                params.config['configurations']['core-site'])
            if is_spnego_enabled(params):
                truncated_core_site.pop('hadoop.http.authentication.type')
                truncated_core_site.pop('hadoop.http.filter.initializers')

            # if there is the viewFS mount table content, create separate xml config and include in in the core-sites
            # else just create core-sites
            if params.mount_table_content:
                XmlConfig(
                    "core-site.xml",
                    conf_dir=params.ams_collector_conf_dir,
                    configurations=truncated_core_site,
                    configuration_attributes=params.
                    config['configurationAttributes']['core-site'],
                    owner=params.ams_user,
                    group=params.user_group,
                    mode=0644,
                    xml_include_file=os.path.join(
                        params.ams_collector_conf_dir,
                        params.xml_inclusion_file_name))

                File(
                    os.path.join(params.ams_collector_conf_dir,
                                 params.xml_inclusion_file_name),
                    owner=params.ams_user,
                    group=params.user_group,
                    content=params.mount_table_content,
                    mode=0644)

                XmlConfig(
                    "core-site.xml",
                    conf_dir=params.hbase_conf_dir,
                    configurations=truncated_core_site,
                    configuration_attributes=params.
                    config['configurationAttributes']['core-site'],
                    owner=params.ams_user,
                    group=params.user_group,
                    mode=0644,
                    xml_include_file=os.path.join(
                        params.hbase_conf_dir, params.xml_inclusion_file_name))

                File(
                    os.path.join(params.hbase_conf_dir,
                                 params.xml_inclusion_file_name),
                    owner=params.ams_user,
                    group=params.user_group,
                    content=params.mount_table_content,
                    mode=0644)
            else:
                XmlConfig(
                    "core-site.xml",
                    conf_dir=params.ams_collector_conf_dir,
                    configurations=truncated_core_site,
                    configuration_attributes=params.
                    config['configurationAttributes']['core-site'],
                    owner=params.ams_user,
                    group=params.user_group,
                    mode=0644)

                XmlConfig(
                    "core-site.xml",
                    conf_dir=params.hbase_conf_dir,
                    configurations=truncated_core_site,
                    configuration_attributes=params.
                    config['configurationAttributes']['core-site'],
                    owner=params.ams_user,
                    group=params.user_group,
                    mode=0644)

        if params.metric_collector_https_enabled:
            export_ca_certs(params.ams_collector_conf_dir)

        pass

    elif name == 'monitor':

        # TODO Uncomment when SPNEGO support has been added to AMS service check and Grafana.
        if is_spnego_enabled(params) and is_redhat_centos_6_plus():
            try:
                import kerberos
            except ImportError:
                raise ImportError(
                    "python-kerberos package need to be installed to run AMS in SPNEGO mode"
                )

        Directory(
            params.ams_monitor_conf_dir,
            owner=params.ams_user,
            group=params.user_group,
            create_parents=True)

        Directory(
            params.ams_monitor_log_dir,
            owner=params.ams_user,
            group=params.user_group,
            mode=0755,
            create_parents=True)

        if params.host_in_memory_aggregation and params.log4j_props is not None:
            File(
                format("{params.ams_monitor_conf_dir}/log4j.properties"),
                mode=0644,
                group=params.user_group,
                owner=params.ams_user,
                content=InlineTemplate(params.log4j_props))

            XmlConfig(
                "ams-site.xml",
                conf_dir=params.ams_monitor_conf_dir,
                configurations=params.config['configurations']['ams-site'],
                configuration_attributes=params.
                config['configurationAttributes']['ams-site'],
                owner=params.ams_user,
                group=params.user_group)
            XmlConfig(
                "ssl-server.xml",
                conf_dir=params.ams_monitor_conf_dir,
                configurations=params.config['configurations']
                ['ams-ssl-server'],
                configuration_attributes=params.
                config['configurationAttributes']['ams-ssl-server'],
                owner=params.ams_user,
                group=params.user_group)
            pass

        Execute(
            format(
                "{sudo} chown -R {ams_user}:{user_group} {ams_monitor_log_dir}"
            ))

        Directory(
            params.ams_monitor_pid_dir,
            owner=params.ams_user,
            group=params.user_group,
            cd_access="a",
            mode=0755,
            create_parents=True)

        Directory(
            format("{ams_monitor_dir}/psutil/build"),
            owner=params.ams_user,
            group=params.user_group,
            cd_access="a",
            create_parents=True)

        Execute(
            format(
                "{sudo} chown -R {ams_user}:{user_group} {ams_monitor_dir}"))

        TemplateConfig(
            format("{ams_monitor_conf_dir}/metric_monitor.ini"),
            owner=params.ams_user,
            group=params.user_group,
            template_tag=None)

        TemplateConfig(
            format("{ams_monitor_conf_dir}/metric_groups.conf"),
            owner=params.ams_user,
            group=params.user_group,
            template_tag=None)

        File(
            format("{ams_monitor_conf_dir}/ams-env.sh"),
            owner=params.ams_user,
            content=InlineTemplate(params.ams_env_sh_template))

        if params.metric_collector_https_enabled or params.is_aggregation_https_enabled:
            export_ca_certs(params.ams_monitor_conf_dir)

        pass
    elif name == 'grafana':

        ams_grafana_directories = [
            params.ams_grafana_conf_dir, params.ams_grafana_log_dir,
            params.ams_grafana_data_dir, params.ams_grafana_pid_dir
        ]

        for ams_grafana_directory in ams_grafana_directories:
            Directory(
                ams_grafana_directory,
                owner=params.ams_user,
                group=params.user_group,
                mode=0755,
                create_parents=True,
                recursive_ownership=True)

        File(
            format("{ams_grafana_conf_dir}/ams-grafana-env.sh"),
            owner=params.ams_user,
            group=params.user_group,
            content=InlineTemplate(params.ams_grafana_env_sh_template))

        File(
            format("{ams_grafana_conf_dir}/ams-grafana.ini"),
            owner=params.ams_user,
            group=params.user_group,
            content=InlineTemplate(params.ams_grafana_ini_template),
            mode=0600)

        if action != 'stop':
            for dir in ams_grafana_directories:
                Execute(('chown', '-R', params.ams_user, dir), sudo=True)

        if params.metric_collector_https_enabled:
            export_ca_certs(params.ams_grafana_conf_dir)

        pass
    generate_logfeeder_input_config(
        'ambari-metrics',
        Template(
            "input.config-ambari-metrics.json.j2", extra_imports=[default]))


def is_spnego_enabled(params):
    if 'core-site' in params.config['configurations'] \
            and 'hadoop.http.authentication.type' in params.config['configurations']['core-site'] \
            and params.config['configurations']['core-site']['hadoop.http.authentication.type'] == "kerberos" \
            and 'hadoop.http.filter.initializers' in params.config['configurations']['core-site'] \
            and "org.apache.hadoop.security.AuthenticationFilterInitializer" in \
                    params.config['configurations']['core-site']['hadoop.http.filter.initializers']:
        return True
    return False


def is_redhat_centos_6_plus():
    import platform

    if platform.dist()[0] in ['redhat', 'centos'
                              ] and platform.dist()[1] > '6.0':
        return True
    return False


def export_ca_certs(dir_path):
    # export ca certificates on every restart to handle changed truststore content

    import params
    import tempfile

    ca_certs_path = os.path.join(dir_path, params.metric_truststore_ca_certs)
    truststore = params.metric_truststore_path

    tmpdir = tempfile.mkdtemp()
    truststore_p12 = os.path.join(tmpdir, 'truststore.p12')

    if (params.metric_truststore_type.lower() == 'jks'):
        for alias in params.metric_truststore_alias_list:
            # Convert truststore from JKS to PKCS12
            cmd = format(
                "{sudo} {java64_home}/bin/keytool -importkeystore -srckeystore {metric_truststore_path} -destkeystore {truststore_p12} -srcalias "
                + alias +
                " -deststoretype PKCS12 -srcstorepass {metric_truststore_password} -deststorepass {metric_truststore_password}"
            )
            Execute(cmd, )
        truststore = truststore_p12

    # Export all CA certificates from the truststore to the conf directory
    cmd = format(
        "{sudo} openssl pkcs12 -in {truststore} -out {ca_certs_path} -cacerts -nokeys -passin pass:{metric_truststore_password}"
    )
    Execute(cmd, )
    Execute(('chown', format('{ams_user}:{user_group}'), ca_certs_path),
            sudo=True)
    Execute(
        ('chmod', '644', ca_certs_path),
        sudo=True,
    )
    Execute(format('{sudo} rm -rf {tmpdir}'))

    pass
